/*******************************************************************************
* Copyright 2017 ROBOTIS CO., LTD.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/

/* Author: Ryu Woon Jung (Leon) */


#include "sync_read.h"

typedef struct
{
  uint8_t     id;
  uint8_t     data[DATA_MAX_LEN];
}DataList;

typedef struct
{
  int         data_list_length;

  uint8_t     last_result;
  uint8_t     is_param_changed;

  uint16_t    start_address;
  uint16_t    data_length;

  DataList   data_list[TOTAL_MOTOR_NUM];
}GroupData;

static GroupData groupData;

static int size()
{
  int data_num;
  int real_size = 0;

  for (data_num = 0; data_num < groupData.data_list_length; data_num++)
  {
    if (groupData.data_list[data_num].id != NOT_USED_ID)
      real_size++;
  }
  return real_size;
}

static int find(int id)
{
  int data_num;

  for (data_num = 0; data_num < groupData.data_list_length; data_num++)
  {
    if (groupData.data_list[data_num].id == id)
      break;
  }
  return data_num;
}

void groupSyncRead(uint16_t start_address, uint16_t data_length)
{
  groupData.data_list_length = 0;
  groupData.last_result = False;
  groupData.is_param_changed = True;
  groupData.start_address = start_address;
  groupData.data_length = data_length;
  groupSyncReadClearParam();
}

void groupSyncReadMakeParam()
{
  int data_num, idx;

  if (size() == 0)
    return;
  idx = 0;
  for (data_num = 0; data_num < groupData.data_list_length; data_num++)
  {
    if (groupData.data_list[data_num].id == NOT_USED_ID)
      continue;

    packetData.data_write[idx++] = groupData.data_list[data_num].id;
  }
}

uint8_t groupSyncReadAddParam(uint8_t id)
{
  int data_num = 0;

  if (id == NOT_USED_ID)
    return False;

  if (groupData.data_list_length != 0)
    data_num = find(id);

  if (groupData.data_list_length == data_num)
  {
    groupData.data_list_length++;

    groupData.data_list[data_num].id = id;
  }

  groupData.is_param_changed = True;
  return True;
}
void groupSyncReadRemoveParam(uint8_t id)
{
  int data_num = find(id);

  if (groupData.data_list[data_num].id == NOT_USED_ID)  // NOT exist
    return;
  groupData.data_list[data_num].id = NOT_USED_ID;
  groupData.is_param_changed = True;
}
void groupSyncReadClearParam()
{
  uint32_t i;
  if (size() == 0)
    return;
  groupData.data_list_length = 0;
  groupData.is_param_changed = False;
  for(i=0; i<TOTAL_MOTOR_NUM; i++)
  {
	  groupData.data_list[i].id = NOT_USED_ID;
  }
}

void groupSyncReadTxPacket()
{
  if (size() == 0)
  {
    packetData.communication_result = COMM_NOT_AVAILABLE;
    return;
  }

  if (groupData.is_param_changed == True)
    groupSyncReadMakeParam();

  syncReadTx(groupData.start_address
    , groupData.data_length
    , (size() * 1));
}

void groupSyncReadRxPacket()
{
  int data_num, c;

  groupData.last_result = False;
  packetData.communication_result = COMM_RX_FAIL;

  if (size() == 0)
  {
    packetData.communication_result = COMM_NOT_AVAILABLE;
    return;
  }

  for (data_num = 0; data_num < groupData.data_list_length; data_num++)
  {
    if (groupData.data_list[data_num].id == NOT_USED_ID)
      continue;

    readRx(groupData.data_length);
    if (packetData.communication_result != COMM_SUCCESS)
      return;

    for (c = 0; c < groupData.data_length; c++)
      groupData.data_list[data_num].data[c] = packetData.data_read[c];
  }

  if (packetData.communication_result == COMM_SUCCESS)
    groupData.last_result = True;
}

void groupSyncReadTxRxPacket()
{
  packetData.communication_result = COMM_TX_FAIL;

  groupSyncReadTxPacket();
  if (packetData.communication_result != COMM_SUCCESS)
    return;
  groupSyncReadRxPacket();
}

uint8_t groupSyncReadIsAvailable(uint8_t id, uint16_t address, uint16_t data_length)
{
  int data_num = find(id);

  if (groupData.last_result == False || groupData.data_list[data_num].id == NOT_USED_ID)
    return False;

  if (address < groupData.start_address || groupData.start_address + groupData.data_length - data_length < address) {
    return False;
  }
  return True;
}

uint32_t groupSyncReadGetData(uint8_t id, uint16_t address, uint16_t data_length)
{
  int data_num = find(id);

  if (groupSyncReadIsAvailable(id, address, data_length) == False)
    return 0;

  switch (data_length)
  {
    case 1:
      return groupData.data_list[data_num].data[address - groupData.start_address];

    case 2:
      return DXL_MAKEWORD(groupData.data_list[data_num].data[address - groupData.start_address], groupData.data_list[data_num].data[address - groupData.start_address + 1]);

    case 4:
      return DXL_MAKEDWORD(
        DXL_MAKEWORD(
        groupData.data_list[data_num].data[address - groupData.start_address + 0]
        , groupData.data_list[data_num].data[address - groupData.start_address + 1])
        , DXL_MAKEWORD(
          groupData.data_list[data_num].data[address - groupData.start_address + 2]
          , groupData.data_list[data_num].data[address - groupData.start_address + 3])
      );

    default:
      return 0;
  }
}
